package com.kwan.xframe.mvvm.data.source.http;

import android.util.Log;

import com.blankj.utilcode.util.NetworkUtils;
import com.blankj.utilcode.util.TimeUtils;
import com.kwan.xframe.mvvm.data.source.http.converter.XGsonConverterFactory;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okhttp3.logging.HttpLoggingInterceptor;
import okio.Buffer;
import okio.BufferedSource;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
import timber.log.Timber;

import static okhttp3.internal.http.HttpHeaders.hasBody;

public class HttpRetrofitClient {


	//超时时间
	private final static int DEFAULT_TIMEOUT = 5;
	protected static final String ACCEPT_TYPE_JSON = "application/json";
	protected static final String ACCEPT_TYPE_XML = "application/xml";
	protected static final String ACCEPT_TYPE_ALL = "*/*";
	private final String mBaseUrl;
	private final Map<String, String> mHeaders;

	public Map<String, String> setHeadValue(String key, String value) {
		if (mHeaders != null) {
			mHeaders.put(key, value);
		}
		return mHeaders;
	}

	public HttpLoggingInterceptor.Level level = HttpLoggingInterceptor.Level.BODY;

	public HttpRetrofitClient setLogLevel(HttpLoggingInterceptor.Level logLevel) {
		level = logLevel;
		return this;
	}

	private boolean isUseCache = true;

	public HttpRetrofitClient setUseCache(boolean isUseCache) {
		this.isUseCache = isUseCache;
		return this;
	}


	public HttpRetrofitClient(String baseUrl, Map<String, String> headers) {
		mBaseUrl = baseUrl;
		mHeaders = headers;
	}

	public WebSocket getWebSocket(WebSocketListener listener) {
		WebSocket webSocket;
		try {
			OkHttpClient client = buildOkHttpClient();
			Request request = new Request.Builder().url(mBaseUrl).build();
			webSocket = client.newWebSocket(request, listener);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return webSocket;
	}


	public OkHttpClient buildOkHttpClient() {

		HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
		loggingInterceptor.setLevel(level);

		OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
				.retryOnConnectionFailure(false)
				.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
				.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
				.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
				.callTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
				.protocols(Collections.singletonList(Protocol.HTTP_1_1))
				.addNetworkInterceptor(loggingInterceptor)
				.addNetworkInterceptor(withHeaderInterceptor());

		if (isUseCache) {
			okHttpClientBuilder.addInterceptor(withCacheInterceptor());
		}

		return okHttpClientBuilder.build();
	}


	public static HashMap<String, String> createCommonHeader() {
		HashMap<String, String> map = new HashMap<>();
		map.put("Accept", ACCEPT_TYPE_ALL);
		map.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
		return map;
	}

	public static String TOKEN_KEY;

	public static HashMap<String, String> createCommonTokenHeader(String tokenKey,String token) {
		TOKEN_KEY = tokenKey;
		HashMap<String, String> map = new HashMap<>();
		map.put("Accept", ACCEPT_TYPE_ALL);
		map.put(tokenKey, token);
		map.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
		return map;
	}


	private Retrofit mRetrofit;

	protected Retrofit getRetrofit() {

		if (mRetrofit == null) {

//			File httpCacheDirectory = new File(BaseApplication.getInstance().getCacheDir(),
//					"responses_cache");
//			int cacheSize = 10 * 1024 * 1024;// 10 MiB
//			Cache cache = new Cache(httpCacheDirectory, cacheSize);

//			Gson: com.squareup.retrofit2:converter-gson
//			Jackson: com.squareup.retrofit2:converter-jackson
//			Moshi: com.squareup.retrofit2:converter-moshi
//			Protobuf: com.squareup.retrofit2:converter-protobuf
//			Wire: com.squareup.retrofit2:converter-wire
//			Simple XML: com.squareup.retrofit2:converter-simplexml
//			Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars


			// 注意 ScalarsConverterFactory 要放在 XGsonConverterFactory 前面添加 否则可能会收不到 onNext

			mRetrofit = new Retrofit.Builder()
					.baseUrl(mBaseUrl)
					.addConverterFactory(ScalarsConverterFactory.create())
					.addConverterFactory(XGsonConverterFactory.create())
					//.addConverterFactory(GsonConverterFactory.create())
					.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
					.client(buildOkHttpClient())
					.build();
		}

		return mRetrofit;
	}


	public static class LoggingInterceptor implements Interceptor {

		private final Charset UTF8 = StandardCharsets.UTF_8;

		@Override
		public Response intercept(Chain chain) throws IOException {
			Timber.e("---- Request ----");
			Request request = chain.request();
			RequestBody requestBody = request.body();
			String body = "";

			if (requestBody != null) {
				Buffer buffer = new Buffer();
				requestBody.writeTo(buffer);
				Charset charset = UTF8;
				MediaType contentType = requestBody.contentType();
				if (contentType != null) {
					charset = contentType.charset(UTF8);
				}
				body = buffer.readString(charset);
			}

			String s = String.format("%s ----> %s \nheaders: %s \nbody：%s",
					request.method(),
					request.url(),
					request.headers(),
					body);
			Log.e("LoggingInterceptor", s);

			long startNs = System.nanoTime();
			String strRequestMethod = request.method();
			String strRequestTime = TimeUtils.getNowString();
			String strRequestBody = body;
			Response response = chain.proceed(request);
			Timber.e("----- Response -----");

			long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);

			ResponseBody responseBody = response.body();
			String rBody = "";

			if (hasBody(response)) {

				BufferedSource source = responseBody.source();
				source.request(Long.MAX_VALUE); // Buffer the entire body.
				Buffer buffer = source.buffer();

				Charset charset = UTF8;
				MediaType contentType = responseBody.contentType();
				if (contentType != null) {
					try {
						charset = contentType.charset(UTF8);
					} catch (UnsupportedCharsetException e) {
						e.printStackTrace();
					}
				}
				rBody = buffer.clone().readString(charset);
			}
			s = String.format("%s %s (%sms) <---- %s \n请求body：%s \n响应body：%s",
					response.code(),
					response.message(),
					tookMs,
					response.request().url(),
					body,
					rBody);

			Log.e("LoggingInterceptor", s);
//			onHttpLogMsg(response.request().url().toString(),
//					strRequestMethod, strRequestTime, strRequestBody,
//					String.valueOf(response.code()), TimeUtils.getNowString(), rBody);


			return response;
		}
	}


	/**
	 * create you ApiService
	 * Create an implementation of the API endpoints defined by the {@code service} interface.
	 */
	public <T> T create(final Class<T> service) {
		if (service == null) {
			throw new RuntimeException("Api service is null!");
		}
		return getRetrofit().create(service);
	}


	protected Interceptor withHeaderInterceptor() {

		return chain -> {

			Request original = chain.request();
			Request.Builder builder = original.newBuilder();

			if (mHeaders != null && mHeaders.size() > 0) {
				Set<String> keys = mHeaders.keySet();
				for (String headerKey : keys) {
					builder.addHeader(headerKey, mHeaders.get(headerKey)).build();
				}
			}

			Request request = builder.method(original.method(), original.body())
					.build();
			return chain.proceed(request);
		};
	}


	protected Interceptor withCacheInterceptor() {

		return chain -> {

			CacheControl.Builder cacheBuilder = new CacheControl.Builder();
			cacheBuilder.maxAge(0, TimeUnit.SECONDS);
			cacheBuilder.maxStale(365, TimeUnit.DAYS);
			CacheControl cacheControl = cacheBuilder.build();

			Request request = chain.request();

			if (!NetworkUtils.isAvailable()) {
				request = request.newBuilder()
						.cacheControl(cacheControl)
						.build();
			}

			Response originalResponse = chain.proceed(request);

			if (NetworkUtils.isAvailable()) {
				int maxAge = 0;// read from cache
				return originalResponse.newBuilder()
						.removeHeader("Pragma")
						.removeHeader("Cache-Control")
						.header("Cache-Control", "public ,max-age=" + maxAge)
						.build();
			} else {
				int maxStale = 60 * 60 * 24 * 28;// tolerate 4-weeks stale
				return originalResponse.newBuilder()
						.removeHeader("Pragma")
						.removeHeader("Cache-Control")
						.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
						.build();
			}
		};
	}


}
